exception Requete_en_cours
exception Pas_de_requete
module AppelEffectif = Appel.Make (Carte)
module EcartEffectif = Ecart.Make (Carte)
type reponse = 
  | Accepte | Refuse | Erreur
type t = 
    {
      mutable informer: ((string -> unit) * int) list;
      mutable i: int;
      mutable p: Carte.carte Partie.partie;
      interdit: bool array array; (* 6 lignes, 78 colonnes. false par défaut. *)
      mutable joueur_appele: int option;
      mutable cartes_non_jouees: Carte.carte list; (* Toutes les cartes de l'écart sont là-dedans *)
      mutable plis: (int * Carte.carte * int) list array;
      (* joueur - carte posée - gagnant *)
    }

let compter fonction liste =
  let rec aux = function
    | [] -> 0
    | a :: b when fonction a -> 1 + aux b
    | _ :: b -> aux b
  in
  aux liste
      
let points j joueur_appele =
  let afficher = false in
  let () =
    if afficher
    then Printf.printf
  	   "\nCalcul des points de %s (%d)...\n%!"
  	   (Partie.string_of_joueur j.p)
  	   (match Partie.mon_numero j.p with None -> (-1) | Some x -> x) in
  (* J'ai droit aux points du chien si je suis le preneur ou le joueur appelé et que j'ai fait au plus une garde sans, ou si je suis dans la défense et que le contrat est une garde contre. *)
  let points_du_chien =
    match j.cartes_non_jouees, (Partie.ecart_connu j.p) with
    | ([a ; b ; c], _)
    | (_, Some [a ; b ; c]) -> (* Je connais le chien (l'écart...). *)
       let () =
	 if afficher
	 then Printf.printf "Je connais le chien : %s, %s, %s. "
       			    (Carte.string_of_carte a)
       			    (Carte.string_of_carte b)
       			    (Carte.string_of_carte c) in
       let dp = List.map (Carte.demipoints) [a ; b ; c] in
       let (dp, bouts) = (List.fold_left (+) 0 dp, compter (Carte.est_bout) [a ; b ; c]) in
       let () =
	 if afficher
	 then Printf.printf
       		  "Ça fait %f points et %d bouts.\n%!"
       		  ((float_of_int dp) /. 2.)
       		  bouts in
       (dp, bouts)
    | _ ->
       let () =
	 if afficher
	 then Printf.printf "Je ne tiens pas encore compte du chien.\n%!" in
       (0, 0)
  in
  let points_d_office =
    match (Partie.preneur j.p, Partie.mon_numero j.p, joueur_appele, Partie.enchere_maximale j.p) with
    | (Some preneur, Some moi, Some appele, (_, prise)) 
	 when (moi = preneur || moi = appele) && prise <= 3 ->
       let () =
	 if afficher
	 then Printf.printf "Je pars avec les points du chien d'office.\n%!" in
       points_du_chien
    | (Some preneur, Some moi, None, (_, prise)) (* mon allié n'est pas encore apparu *)
	 when moi = preneur && prise <= 3 ->
       let () =
	 if afficher
	 then Printf.printf "Je suis le preneur : je dispose des points du chien.\n%!" in
       points_du_chien
    | (Some preneur, Some moi, Some appele, (_, prise))
	 when moi <> preneur && moi <> appele && prise >= 4 ->
       let () =
	 if afficher
	 then Printf.printf "Garde contre : le chien est pour moi.\n%!" in
       points_du_chien
    | (Some preneur, Some moi, None, (_, prise)) (*si j'étais le joueur appelé, je le saurais *)
	 when moi <> preneur && prise >= 4 ->
       let () =
	 if afficher
	 then Printf.printf "Garde contre, mais on ne connaît pas le joueur appelé : le chien est pour moi.\n%!" in
       points_du_chien
    | _ -> (0, 0)
  in
  let mon_equipe = 
    match (Partie.preneur j.p, Partie.mon_numero j.p, joueur_appele) with
    | (Some preneur, Some moi, Some appele) 
	 when moi = preneur || moi = appele ->
       let () =
	 if afficher
	 then Printf.printf "J'attaque.\n%!" in
       [preneur ; appele]
    | (Some preneur, _, Some appele) (* je ne suis pas un attaquant *) ->
       let liste = 
	 List.filter (fun j -> j <> preneur && j <> appele) [0;1;2;3;4]
       in
       let () =
	 if afficher
	 then Printf.printf "Je défends, avec %s.\n%!"
       			      (String.concat ", " (List.map (string_of_int) liste))
       in
       liste
    | (_, Some moi, _) ->
       let () =
	 if afficher
	 then Printf.printf "Pour l'instant, je suis tout seul.\n%!" in
       [moi]
    | _ -> []
  in
  let (++) (x1, x2) (y1, y2) = (x1 + y1, x2 + y2) in
  let somme = ref points_d_office in
  for i=0 to -1 + Partie.nombre_plis_joues j.p do
    List.iter
      (fun (_, c, joueur) ->
       if List.exists ((=) joueur) mon_equipe then
	 let () =
	   if afficher
	   then Printf.printf
	 	  "Le joueur %d est dans mon équipe : je remporte %s.\n%!"
	 	  joueur (Carte.string_of_carte c) in
	 somme := !somme ++ (Carte.demipoints c, if Carte.est_bout c then 1 else 0);) 
      j.plis.(i)
  done;
  !somme

let aux t msg =
  let nom = Partie.string_of_joueur t.p in
  let message = nom^" : "^msg in
  let transmettre (fonction, _) =
    fonction message
  in
  List.iter (transmettre) t.informer
let est_connecte j = 
  let ok = Partie.est_connecte j.p in
  let () = 
    if ok then
      aux j "je suis connecté."
    else
      aux j "je ne suis pas connecté."
  in
  ok
let mon_nom j = 
  let nom = Partie.string_of_joueur j.p in
  nom
let joueurs_disponibles j = 
  let joueurs = Partie.joueurs_disponibles j.p in
  let () =
    match joueurs with
    | [] ->
       aux j "aucun joueur disponible."
    | [joueur] ->
       aux j ("un seul joueur disponible : "^joueur^".")
    | liste ->
       aux j ((string_of_int (List.length liste))
	      ^" joueurs disponibles : "
	      ^(String.concat ", " liste)
	      ^".")
  in
  joueurs
let mes_adversaires j = 
  let noms = Partie.mes_adversaires j.p in
  let () =
    let noms_liste = Array.to_list noms in
    aux j ("noms des joueurs à ma table : "
	   ^(String.concat ", puis " noms_liste)
	   ^".")
  in
  noms
let en_jeu j = 
  let ok = Partie.en_jeu j.p in
  let () =
    if ok then aux j "je suis en cours de partie."
    else aux j "je ne suis pas en train de jouer."
  in
  ok
let mon_numero j = 
  let n = Partie.mon_numero j.p in
  let () =
    match n with
    | None -> aux j "je n'ai pas encore de numéro."
    | Some n -> aux j ("mon numéro est "^(string_of_int n)^".")
  in
  n
let enchere_de j i = 
  let e = Partie.enchere_de j.p i in
  let () =
    match e with
    | None -> aux j ((string_of_int i)^" n'a pas encore fait d'enchère.")
    | Some 0 -> aux j ((string_of_int i)^" a passé.")
    | Some 1 -> aux j ((string_of_int i)^" a prisé.")
    | Some 2 -> aux j ((string_of_int i)^" a gardé.")
    | Some 3 -> aux j ((string_of_int i)^" a gardé sans le chien.")
    | Some 4 -> aux j ((string_of_int i)^" a gardé contre le chien.")
    | Some x -> aux j ((string_of_int i)^" a fait une drôle d'enchère : "
		       ^(string_of_int x)^".")
  in
  e
let preneur j = 
  let p = Partie.preneur j.p in
  let () =
    match p with
    | None -> aux j "pas de preneur déclaré."
    | Some x -> aux j ("le preneur est "^(string_of_int x)^".")
  in
  p
let carte_appelee j = 
  let c = Partie.carte_appelee j.p in
  let () =
    match c with
    | None -> aux j "pas de carte appelée."
    | Some x -> aux j ("la carte appelée est la carte "
		       ^(Carte.string_of_carte x)^".")
  in
  c
let ecart_connu j = 
  let e = Partie.ecart_connu j.p in
  let () =
    match e with
    | None -> aux j "l'écart n'a pas encore été fait."
    | Some [] -> aux j "l'écart a été effectué, mais vous ne le connaissez pas."
    | Some [c1] -> aux j ("je connais une carte de l'écart : "
			  ^(Carte.string_of_carte c1)^".")
    | Some [c1 ; c2] -> aux j ("je connais deux cartes de l'écart : "
			       ^(Carte.string_of_carte c1)^" et "
			       ^(Carte.string_of_carte c2)^".")
    | Some [c1 ; c2 ; c3] -> aux j ("l'écart effectué est "
				    ^(Carte.string_of_carte c1)^", "
				    ^(Carte.string_of_carte c2)^" et "
				    ^(Carte.string_of_carte c3)^".")
    | Some liste -> aux j ("il y a bizarrement "^(string_of_int (List.length liste))
			   ^" cartes dans l'écart : "
			   ^(String.concat ", " (List.map (Carte.string_of_carte) liste))
			   ^".")
  in
  e
let mon_jeu j = 
  let main = Partie.mon_jeu j.p in
  let () = 
    let rec inserer liste carte =
      match liste with
      | [] -> [carte]
      | a :: b when (Carte.int_of_carte a) < (Carte.int_of_carte carte) -> 
	 a :: (inserer b carte)
      | _ -> 
	 carte :: liste
    in
    let main = List.fold_left (inserer) [] main in
    match main with
    | [] -> aux j "je n'ai pas encore reçu mes cartes."
    | [x] -> aux j ("je n'ai qu'une carte : "^(Carte.string_of_carte x)^".")
    | liste ->
       aux j ("je dispose de "^(string_of_int (List.length liste))^" cartes : "
	      ^(String.concat ", " (List.map (Carte.string_of_carte) liste))^".")
  in
  main
let chien j = 
  let c = Partie.chien j.p in
  let () = 
    match c with
    | [] -> aux j "le chien n'a pas été dévoilé."
    | [a ; b ; c] -> 
       aux j ("le chien contient ces trois cartes : "
	      ^(Carte.string_of_carte a)^", "
	      ^(Carte.string_of_carte b)^", et "
	      ^(Carte.string_of_carte c)^".")
    | liste ->
       aux j ("bizarre : le chien comprend "
	      ^(string_of_int (List.length liste))
	      ^" carte(s) : "
	      ^(String.concat ", " (List.map (Carte.string_of_carte) liste))
	      ^".")
  in
  c
let repartition_cartes j i_pli = 
  let r = 
    if i_pli >= 0 && i_pli < 15 
    then j.plis.(i_pli) 
    else [] in
  let () =
    let string_of (poseur, carte, ramasseur) =
      let nom_poseur = (Partie.mes_adversaires j.p).(poseur) in
      let nom_carte = Carte.string_of_carte carte in
      let nom_ramasseur = (Partie.mes_adversaires j.p).(ramasseur) in
      nom_poseur^" ("^(string_of_int poseur)^") a posé "^nom_carte
      ^", qui sera ramassée par "^nom_ramasseur^"("
      ^(string_of_int ramasseur)^")"
    in
    aux j ("voici la répartition temporaire des cartes : "
	   ^(String.concat " ; " (List.map (string_of) r))
	   ^".")
  in
  r
let joueur_maitre j = 
  let jm = Partie.joueur_maitre (Carte.sup_large) j.p in
  let () =
    match jm with
    | None -> aux j "pas de joueur maître."
    | Some x ->
       let s_x = string_of_int x in
       let nom = (Partie.mes_adversaires j.p).(x) in
       let _ = repartition_cartes j in
       aux j ("le joueur maître du tapis est "^nom^" ("^s_x^").")
  in
  jm
let pli_termine j = 
  let ok = Partie.pli_termine j.p in
  let () =
    if ok then
      aux j "le pli est terminé."
    else
      aux j "le pli n'est pas terminé."
  in
  ok
let nombre_plis_joues j = 
  let n = Partie.nombre_plis_joues j.p in
  let () =
    let str = string_of_int n in
    match n with
    | 0 -> aux j "aucun pli n'a été joué."
    | 1 -> aux j "un seul pli a été joué."
    | 15 -> aux j "tous les plis ont été joués."
    | _ -> aux j (str^" plis ont été joués.")
  in
  n
let joueur_ouverture j = 
  let n = Partie.joueur_ouverture j.p in
  let () =
    let nom = (Partie.mes_adversaires j.p).(n) in
    let str = string_of_int n in
    aux j ("ouverture faite par "^nom^" ("^str^").")
  in
  n
let requete_en_cours j = 
  let r = Partie.requete_en_cours j.p in
  let () =
    if r then
      aux j "une requête est en cours."
    else
      aux j "pas de requête en cours."
  in
  r
let doit_priser j = 
  let dp = Partie.doit_priser j.p in
  let () = 
    if dp then
      aux j "je dois priser."
    else
      aux j "je n'ai pas à priser."
  in
  dp
let enchere_maximale j = 
  let (preneur, enchere) = Partie.enchere_maximale j.p in
  let () =
    let nom = (Partie.mes_adversaires j.p).(preneur) in
    let str = string_of_int preneur in
    let ench = match enchere with
      | 0 -> "passe" | 2 -> "garde" | 4 -> "garde contre le chien"
      | 1 -> "prise" | 3 -> "garde sans le chien" 
      | x -> "drôle d'enchère ("^(string_of_int x)^")" in
    aux j ("enchère maximale : "^ench^" faite par "^nom^" ("^str^").")
  in
  (preneur, enchere)
let doit_appeler j = 
  let da = Partie.doit_appeler j.p in
  let () = aux j (if da then "je dois appeler une carte" 
		  else "je n'ai pas à appeler une carte.")
  in
  da
let cartes_appelables partie =
  let ca = AppelEffectif.cartes_appelables
	     (Partie.mon_jeu partie.p) in
  let () = 
    let rec inserer liste carte =
      match liste with
      | [] -> [carte]
      | a :: b when (Carte.int_of_carte a) < (Carte.int_of_carte carte) -> 
	 a :: (inserer b carte)
      | _ -> 
	 carte :: liste
    in
    let cartes = List.fold_left (inserer) [] ca in
    match cartes with
    | [] -> aux partie "je ne sais pas quelles cartes je peux appeler."
    | liste ->
       aux partie ("je peux choisir ma carte à appeler parmi "
		   ^(string_of_int (List.length liste))^" cartes : "
		   ^(String.concat ", " (List.map (Carte.string_of_carte) liste))^".")
  in
  ca

let petit_au_bout t =
  if nombre_plis_joues t = 15 then
    let rec chercher_petit = function
      | [] -> None
      | (_, (1, Carte.Atout), gagnant) :: _ ->
	 let res = 
	   match preneur t, t.joueur_appele with
	   | (None, _) -> (* Impossible, on a joué 15 tours *) 
	      failwith "Malgré les 15 tours joués, on n'a pas trouvé le preneur."
	   | (_, None) ->
	      failwith "Malgré les 15 tours joués, on n'a pas trouvé le joueur appelé."
	   | (Some preneur, Some appele) ->
	      let attaque_pab = gagnant = preneur || gagnant = appele in
	      Some
		(if attaque_pab then `Attaque else `Defense)
	 in res
      | _ :: reste -> chercher_petit reste
    in
    chercher_petit (t.plis.(14))
  else None

let peut_appeler partie carte =
  let pa = List.exists (fun c -> c = carte)
		       (cartes_appelables partie) in
  let () =
    let nom = Carte.string_of_carte carte in
    aux partie ((if pa then "je peux appeler " else "je ne peux pas appeler ")
		^nom^".")
  in
  pa
let joueur_appele partie = partie.joueur_appele
let doit_ecarter j = 
  let da = Partie.doit_ecarter j.p in
  let () =
    aux j (if da then "je dois faire un écart." 
	   else "je n'ai pas à faire un écart.")
  in
  da
let difference l1 l2 =
  let rec est_absent x = function
    | [] -> true
    | hd :: _ when hd = x -> false
    | _ :: tl -> est_absent x tl
  in
  let rec examiner = function
    | [] -> []
    | a :: b when est_absent a l2 ->
       a :: (examiner b)
    | _ :: b -> examiner b
  in
  examiner l1
let carte_ecartable_marginale partie cartes_fixees =
  let structure = EcartEffectif.structure_ecart
		    (Partie.mon_jeu partie.p) in
  let parmi =
    match structure with
    | EcartEffectif.Parmi (n, liste) ->
       if difference cartes_fixees liste = [] then
	 (* On tape uniquement dans la liste autorisée *)
	 if List.length cartes_fixees >= n then []
	 else difference liste cartes_fixees
       (* Cartes qu'on peut encore écarter *)
       else []
    | EcartEffectif.ToutesEt
	(obligatoires, EcartEffectif.Parmi (n, possibles)) ->
       let obligatoires_restantes_a_ecarter = 
	 difference obligatoires cartes_fixees
       in
       let possibles_restantes = difference possibles cartes_fixees
       in
       let possibles_ecartees = difference possibles possibles_restantes
       in
       (*let obligatoires_ecartees = 
       difference obligatoires 
		  obligatoires_restantes_a_ecarter in *)
       if obligatoires_restantes_a_ecarter = [] then possibles_restantes
       else if List.length possibles_ecartees = n then
	 (* On ne peut plus écarter que les obligatoires *)
	 obligatoires_restantes_a_ecarter
       else if List.length possibles_ecartees > n then
				       (* On a écarté trop d'atouts *) []
       else (* On peut encore écarter des atouts *)
	 obligatoires_restantes_a_ecarter @ possibles_restantes
    | _ -> failwith "La structure d'écart est mal formée"
  in
  let () =
    let noms_fixes = List.map (Carte.string_of_carte) cartes_fixees in
    let noms_possibles = List.map (Carte.string_of_carte) parmi in
    let en_fixant =
      match noms_fixes with
      | [] -> "sans rien fixer, "
      | [x] -> "en fixant la carte "^x^", "
      | liste -> "en fixant les "^(string_of_int (List.length liste))
		 ^" cartes "^(String.concat ", " liste)^", "
    in
    let possibles =
      match noms_possibles with
      | [] -> "je ne peux écarter aucune carte supplémentaire."
      | [x] -> "je ne peux écarter que "^x^"."
      | liste -> "je peux ensuite écarter une carte parmi les "
		 ^(string_of_int (List.length liste))^" cartes "
		 ^(String.concat ", " liste)^"."
    in
    let msg = en_fixant^possibles in
    aux partie msg
  in
  parmi
let peut_ecarter partie cartes =
  let s = EcartEffectif.structure_ecart 
	    (Partie.mon_jeu partie.p) in
  let ok = EcartEffectif.respecte_structure cartes s in
  let () =
    let noms = List.map (Carte.string_of_carte) cartes in
    let chaine = String.concat ", " noms in
    let n = string_of_int (List.length cartes) in
    let msg = 
      if ok then
	match n with
	| "0" -> "je peux faire un écart vide..."
	| "1" -> "je peux écarter la carte "^chaine^"..."
	| _ -> "je peux écarter les "^n^" cartes "^chaine^"."
      else
	match n with
	| "0" -> "je ne peux pas faire un écart vide..."
	| "1" -> "je ne peux pas écarter la carte "^chaine^"..."
	| _ -> "je ne peux pas écarter les "^n^" cartes "^chaine^"."
    in
    aux partie msg
  in
  ok
let devant_jouer j = Partie.devant_jouer j.p
let doit_jouer j = 
  let ok = Partie.doit_jouer j.p in
  let () =
    if ok then aux j "je dois jouer."
    else aux j "je n'ai pas à jouer."
  in
  ok
let couleur_demandee j =
  Partie.couleur_demandee (snd) Carte.excuse j.p
let plus_gros_atout j = Partie.plus_gros_atout (Carte.est_atout) (fst) j.p
let interdictions j =
  Array.map (Array.copy) j.interdit
let cartes_jouables j =
  let c = Partie.cartes_jouables (Carte.est_atout) (fst) (snd) 
				 Carte.excuse j.p in
  (* On interdit la couleur appelée sauf le roi appelé si on est le premier joueur (ou le deuxième derrière l'excuse) au premier tour *)
  let c = 
    if Partie.nombre_plis_joues j.p = 0 &&
	 (Partie.mon_numero j.p = Some 0
	  || (Partie.mon_numero j.p = Some 1
	      && match repartition_cartes j 0 with
		 | [(_, exc, _) ; (_, fausse_exc, _)]
		      when exc = Carte.excuse
			   && fausse_exc = Carte.fausse_excuse -> true
		 | _ -> false)) then
      match Partie.carte_appelee j.p with
      | None -> c
      | Some (valeur_appelee, couleur_appelee) ->
	 List.filter
	   (fun (valeur, couleur) -> couleur <> couleur_appelee || valeur = valeur_appelee)
	   c
    else c
  in
  let () =
    let rec inserer liste carte =
      match liste with
      | [] -> [carte]
      | a :: b when (Carte.int_of_carte a) < (Carte.int_of_carte carte) -> 
	 a :: (inserer b carte)
      | _ -> 
	 carte :: liste
    in
    let main = List.fold_left (inserer) [] c in
    match main with
    | [] -> aux j "je ne peux rien jouer."
    | [x] -> aux j ("je ne peux jouer que la carte "^(Carte.string_of_carte x)^".")
    | liste ->
       aux j ("je peux jouer l'une des "^(string_of_int (List.length liste))^" cartes : "
	      ^(String.concat ", " (List.map (Carte.string_of_carte) liste))^".")
  in
  c
let peut_jouer partie c =
  let ok = List.exists (fun x -> x = c)
		       (cartes_jouables partie) in
  let () =
    let nom = Carte.string_of_carte c in
    if ok then
      aux partie ("je peux jouer la carte "^nom^".")
    else
      aux partie ("je ne peux pas jouer la carte "^nom^".")
  in
  ok
let chien_max_defaut = 
  let liste = 
    let tab = Array.init 78 (Carte.carte_of_int) in
    Array.to_list tab
  in
  List.filter 
    (fun c -> not (Carte.est_bout c)
      && not (Carte.est_roi c))
    liste
let make () = 
  {
    informer=[];
    i = 0;
    p=Partie.make();
    interdit = Array.make_matrix 6 78 false;
    joueur_appele = None;
    cartes_non_jouees = 
      Array.to_list 
	(Array.init 78 (Carte.carte_of_int)) ;
    plis = Array.make 15 [];
  }
let reinitialiser_interdictions j =
  let main = Partie.mon_jeu j.p in
  let mon_numero =
    match Partie.mon_numero j.p with
    | None -> failwith "Impossible"
    | Some n -> n
  in
  let () =
    (* Je réinitialise *)
    for i=0 to -1 + Array.length j.interdit do
      for k=0 to -1 + Array.length j.interdit.(0) do
	j.interdit.(i).(k) <- false
      done
    done;
    j.joueur_appele <- None
  in
  let () =
    (* On interdit mes cartes pour tout le monde *)
    let i_cartes = List.map (Carte.int_of_carte) main in
    List.iter (fun i ->
	       for k=0 to 5 do
		 j.interdit.(k).(i) <- true
	       done) i_cartes 
  in
  let () =
    (* Je m'interdis toutes les cartes *)
    for i_carte = 0 to 77 do
      j.interdit.(mon_numero).(i_carte) <- true
    done
  in
  let () =
    (* Je m'autorise toutes mes cartes *)
    List.iter (fun carte ->
	       let i = Carte.int_of_carte carte in
	       j.interdit.(mon_numero).(i) <- false)
	      main
  in
  let () =
    (* Si un écart a été fait, j'interdis tous les rois, bouts et atouts au chien *)
    let (_, e) = enchere_maximale j in
    if e > 0 && e < 3 then
      let () =
	for i_col = 0 to 3 do
	  j.interdit.(5).(Carte.int_of_carte
			    (Carte.roi, Carte.couleur_of_int i_col)) <- true
	done
      in
      let () =
	for i = 1 to 21 do
	  j.interdit.(5).(Carte.int_of_carte
			    (i, Carte.Atout)) <- true
	done
      in
      let () =
	j.interdit.(5).(Carte.int_of_carte (Carte.excuse)) <- true
      in
      ()
  in
  let () =
    (* Si je connais 3 cartes de l'écart, j'interdis toutes les cartes au chien *)
    match ecart_connu j with
    | Some [ _ ; _ ; _ ] ->
       for i=0 to -1 + Array.length j.interdit.(5) do
	 j.interdit.(5).(i) <- true
       done
    | _ -> ()
  in
  let () =
    (* Si je connais une partie de l'écart, j'empêche ces cartes d'être interdites au chien et je les interdis pour tout le monde.*)
    match ecart_connu j with
    | Some liste ->
       List.iter (fun c ->
		  let i = Carte.int_of_carte c in
		  for joueur = 0 to -1 + Array.length j.interdit do
		    j.interdit.(joueur).(i) <- true
		  done ;
		  j.interdit.(5).(i) <- false)
		 liste
    | _ -> ()
  in
  ()
    
let traiter_reponse p f = 
  let g = function
    | Partie.Accepte -> f Accepte
    | Partie.Refuse -> f Refuse
    | Partie.Erreur -> f Erreur
  in
  Partie.traiter_reponse p.p (g)
let assimiler_requete_deconnecter j = 
  let () = Partie.assimiler_requete_deconnecter j.p in
  let () = traiter_reponse 
	     j (function Accepte -> aux j "déconnexion acceptée."
		       | Refuse -> aux j "déconnexion refusée."
		       | Erreur -> aux j "déconnexion interrompue.") in
  aux j "je demande à me déconnecter."
let assimiler_requete_identifier j nom = 
  let () = Partie.assimiler_requete_identifier j.p nom in
  let () =
    traiter_reponse
      j (function Accepte -> aux j ("identification acceptée : je m'appelle "
				    ^nom^".")
		| Refuse -> aux j ("identification refusée : je voulais m'appeler "
				   ^nom^".")
		| Erreur -> aux j ("identification interrompue : je voulais m'appeler "
				   ^nom^".")) in
  aux j ("je demande à m'appeler "^nom^".")
let assimiler_requete_inviter j liste =
  let () = Partie.assimiler_requete_inviter j.p liste in
  let noms = String.concat ", " liste in
  let () =
    traiter_reponse
      j (function Accepte -> aux j ("invitation acceptée : je joue avec "^noms^".")
		| Refuse -> aux j ("invitation refusée : je voulais jouer avec "
				   ^noms^".")
		| Erreur -> aux j ("invitation interrompue : je voulais jouer avec "
				   ^noms^".")) in
  let () = aux j ("je demande à inviter "^noms^".") in
  ()
let assimiler_requete_prise j x = 
  let () = Partie.assimiler_requete_prise j.p x in
  let p = match x with
    | 0 -> "passer." | 3 -> "garder sans le chien."
    | 1 -> "priser." | 4 -> "garder contre le chien."
    | 2 -> "garder." 
    | x -> "faire une drôle d'enchère ("^(string_of_int x)^")." in
  let () =
    traiter_reponse
      j (function Accepte -> aux j ("j'ai réussi à "^p)
		| Refuse -> aux j ("je n'ai pas réussi à "^p)
		| Erreur -> aux j ("je n'ai pas réussi à "^p
				   ^" J'ai été déconnecté."))
  in
  aux j ("je demande à "^p)
let assimiler_requete_appel j carte = 
  let () = Partie.assimiler_requete_appel j.p carte in
  let nom = Carte.string_of_carte carte in
  let () = traiter_reponse
	     j (function Accepte -> aux j ("j'ai réussi à appeler "^nom^".")
		       | Refuse -> aux j ("l'appel de "^nom^" a été refusé.")
		       | Erreur -> aux j ("l'appel de "^nom^" a été interrompu.")) in
  aux j ("je demande à appeler "^nom^".")
let assimiler_requete_ecart j liste = 
  let () = Partie.assimiler_requete_ecart j.p liste in
  let noms = String.concat ", " (List.map (Carte.string_of_carte) liste) in
  let () =
    traiter_reponse
      j (function Accepte -> 
	let () = (* J'interdis l'écart pour tout le monde *)
	  let i_cartes = List.map (Carte.int_of_carte) liste in
	  List.iter (fun i ->
	    for k=0 to 4 do
	      j.interdit.(k).(i) <- true
	    done) i_cartes 
	in
	aux j ("j'ai réussi à écarter "^noms^".")
      | Refuse -> aux j ("mon écart ("^noms^") a été refusé.")
      | Erreur -> aux j ("mon écart ("^noms^") a été interrompu."))
  in
  aux j ("je demande à écarter "^noms^".")
let assimiler_requete_carte j c = 
  let () = Partie.assimiler_requete_carte j.p c in
  let nom = Carte.string_of_carte c in
  let () = traiter_reponse
	     j (function Accepte -> aux j ("j'ai réussi à jouer "^nom^".")
		       | Refuse -> aux j ("le jeu de "^nom^" a été refusé.")
		       | Erreur -> aux j ("le jeu de "^nom^" a été interrompu.")) in
  aux j ("je demande à jouer "^nom^".")
let assimiler_information_erreur j = 
  let () = Partie.assimiler_information_erreur j.p in
  aux j "je reçois « erreur »."
let assimiler_information_connexion j = 
  let () = Partie.assimiler_information_connexion j.p in
  aux j "je reçois « connexion »."
let assimiler_information_deconnexion j = 
  let () = Partie.assimiler_information_deconnexion j.p in
  aux j "je reçois « déconnexion »."
let assimiler_information_entree j nom = 
  let () = Partie.assimiler_information_entree j.p nom in
  aux j ("je reçois « entree "^nom^" ».")
let assimiler_information_sortie j nom = 
  let () = Partie.assimiler_information_sortie j.p nom in
  aux j ("je reçois « sortie "^nom^" ».")
let assimiler_information_noms j noms = 
  let () = Partie.assimiler_information_noms j.p noms in
  let liste = Array.to_list noms in
  let () = for i=0 to 14 do j.plis.(i) <- [] done in
  let () = j.cartes_non_jouees <- Array.to_list (Array.init 78 (Carte.carte_of_int)) in
  aux j ("je reçois « noms [|"
	 ^(String.concat " ; " liste)^"|] ».")
let assimiler_information_numero j n = 
  let () = Partie.assimiler_information_numero j.p n in
  aux j ("je reçois « numero "^(string_of_int n)^" ».")
let assimiler_information_contrat j i = 
  let () = Partie.assimiler_information_contrat j.p i in
  let prise = 
    match i with
    | 0 -> "passe" | 1 -> "prise" | 2 -> "garde"
    | 3 -> "garde sans le chien" | 4 -> "garde contre le chien" 
    | x -> "prise_inconnue : "^(string_of_int x)in
  aux j ("je reçois « contrat "^prise^" ».")
let assimiler_information_appel j = 
  let () = Partie.assimiler_information_appel j.p in
  aux j "je reçois « appel »."
let assimiler_information_contrat_final j preneur prise appelee =
  let () =  Partie.assimiler_information_contrat_final 
	      (Carte.string_of_carte) j.p preneur prise appelee in
  let () =
    (* Si j'ai la carte, c'est moi. *)
    if List.exists ((=) appelee) (mon_jeu j) then j.joueur_appele <- mon_numero j
  in
  aux j ("je reçois « contrat_final "^(string_of_int preneur)
	 ^" "^(match prise 
	       with 0 -> "passe" | 1 -> "prise" | 2 -> "garde"
		    | 3 -> "garde sans" | 4 -> "garde contre"
		    | x -> "(prise inconnue : "^(string_of_int x)^")")
	 ^" "^(Carte.string_of_carte appelee)^" ».")
let assimiler_information_atout_au_chien j liste =
  let () = Partie.assimiler_information_atout_au_chien j.p liste in
  let () =
    (* On interdit ces cartes pour tout le monde *)
    let i_cartes = List.map (Carte.int_of_carte) liste in
    List.iter (fun i ->
      for k=0 to 4 do
	j.interdit.(k).(i) <- true
      done) i_cartes 
  in
  aux j ("je reçois « atout ["^(String.concat 
				  " ; " (List.map 
					   (Carte.string_of_carte) liste))
	 ^"] ».")
let assimiler_information_jeu j =
  let () = Partie.assimiler_information_jeu j.p in
  let () = reinitialiser_interdictions j in
  aux j "je reçois « jeu »."
let assimiler_information_distribution j liste = 
  let () = Partie.assimiler_information_distribution (Carte.int_of_carte) j.p liste in
  let () = reinitialiser_interdictions j in
  let rec inserer liste carte =
    match liste with
    | [] -> [carte]
    | a :: b when (Carte.int_of_carte a) < (Carte.int_of_carte carte) -> 
       a :: (inserer b carte)
    | _ -> 
       carte :: liste
  in
  let main = List.fold_left (inserer) [] liste in
  let noms = List.map (Carte.string_of_carte) main in
  let noms = "["^(String.concat "; " noms)^"]" in
  aux j ("je reçois « distribution "^noms^" » (en triant les cartes).")
let assimiler_information_chien j liste =
  (* let () = Printf.printf "J'ai assimilé le message chien.\n%!" in *)
  let () = Partie.assimiler_information_chien (Carte.int_of_carte) j.p liste in
  let noms = List.map (Carte.string_of_carte) liste in
  let noms = "["^(String.concat "; " noms)^"]" in
  let preneur = 
    match Partie.preneur j.p with
    | None -> failwith "Erreur : le preneur n'est pas encore défini !" 
    | Some p -> p
  in
  let () = reinitialiser_interdictions j in
  let () = 
    (* On interdit ces cartes-là pour tout le monde sauf le preneur. *)
    List.iter
      (fun carte ->
       let i = Carte.int_of_carte carte in
       let () =
	 for joueur=0 to 4 do
	   j.interdit.(joueur).(i) <- true
	 done
       in
       j.interdit.(preneur).(i) <- false)
      liste
  in
  aux j ("je reçois « chien "^noms^" ».")
let assimiler_information_pli j joueur =
  (* let () = Printf.printf "Assimilation d'un pli...\n%!" in *)
  let () = Partie.assimiler_information_pli j.p joueur in
  (* let () = Printf.printf "Pli assimilé.\n%!" in *)
  let str = string_of_int joueur in
  aux j ("je reçois « pli "^str^" ».")
let assimiler_information_carte j c =
  let joueur = Partie.devant_jouer j.p in
  let () = Partie.assimiler_information_carte j.p c in
  let () = j.cartes_non_jouees <- List.filter ((<>) c)
    j.cartes_non_jouees in
  (* On enregistre le pli *)
  let r = 
    Partie.repartition_cartes
      (Carte.sup_large) Carte.excuse
      Carte.fausse_excuse j.p
  in
  let () = j.plis.(Partie.nombre_plis_joues j.p) <- r in
  let moi = match Partie.mon_numero j.p with Some m -> m | _ -> 5 in
  let interdire_atouts_superieurs_a carte =
    let valeur = fst carte in
    let atouts_interdits = Array.init 
      (22 - valeur) (fun i -> (21 - i, Carte.Atout)) in
    (* exemple : si carte = 20, on interdit 22 - 20 = 2 carte, le 21 - 0 = 21 et le 21 - 1 = 20 *)
    Array.iter (fun carte ->
		let i_carte = Carte.int_of_carte carte in
		j.interdit.(joueur).(i_carte) <- true)
	       atouts_interdits
  in
  let interdire_couleur_simple couleur =
    let cartes = Array.init 14 (fun i -> (i + 1, couleur)) in
    let i_cartes = Array.map (Carte.int_of_carte) cartes in
    Array.iter (fun i_carte -> 
      j.interdit.(joueur).(i_carte) <- true)
      i_cartes
  in
  let couleur_demandee = couleur_demandee j in
  let () = 
    if joueur <> moi && c <> Carte.excuse then
      match couleur_demandee with
      | None -> ()
      | Some Carte.Excuse ->
	 failwith "La couleur demandée est excuse ! Ça va pas du tout !"
      | Some Carte.Atout -> 
	 if Carte.est_atout c then
	   (* Si c est inférieure au plus gros atout, on enlève tous les autres. *)
	   match plus_gros_atout j with
	   | Some pgo when pgo > (fst c) ->
	      interdire_atouts_superieurs_a (pgo, Carte.Atout)
	   (* Le joueur n'a pas d'atout au-dessus de pgo *)
	   | _ -> () (* On ne peut rien dire *)
	 else (* plus d'atout *)
	   interdire_atouts_superieurs_a Carte.petit
      | Some couleur_demandee when Carte.est_atout c ->
	 (* c'est une couleur simple *)
	 (* on interdit cette couleur *)
	 let () = interdire_couleur_simple couleur_demandee in
	 (* Et si on sous-coupe ? *)
	 let () =
	   match plus_gros_atout j with
	   | Some pgo when pgo > (fst c) ->
	      interdire_atouts_superieurs_a (pgo, Carte.Atout)
	   | _ -> () (* C'est là qu'on se rend compte qu'on a fait let () = () in () *)
	 in ()
      | Some couleur_simple when snd c <> couleur_simple ->
	 (* On pisse *)
	 let () = interdire_couleur_simple couleur_simple in
	 interdire_atouts_superieurs_a Carte.petit (* plus d'atout *)
      | _ -> ()
    else ()
  in
  (* On interdit cette carte pour tout le monde *)
  let i_carte = Carte.int_of_carte c in
  let () = 
    for i=0 to -1 + Array.length j.interdit do
      j.interdit.(i).(i_carte) <- true
    done
  in
  let () =
    (* Si je vois passer le roi appelé... *)
    match (j.joueur_appele, Partie.carte_appelee j.p) with
    | (None, Some appelee) when c = appelee ->
       j.joueur_appele <- Some joueur
    | _ -> ()
  in
  let str = Carte.string_of_carte c in
  aux j ("je reçois « carte "^str^" ».")
let assimiler_information_resultat joueur res =
  let r = 
    "[|"
    ^(String.concat " ; " (List.map (string_of_int) (Array.to_list res)))
    ^"|]"
  in
  aux joueur ("je reçois « resultat "^r^" ».")
let informer t fonction =
  let i = t.i in
  let () = t.i <- 1 + t.i in
  let annuler () = 
    t.informer <- List.filter
		    (fun (_, id) -> id <> i)
		    t.informer
  in
  t.informer <- (fonction, i) :: t.informer ;
  annuler

type etat =
  {
    connecte: bool;
    mon_nom: string;
    en_jeu: bool;
    joueurs_disponibles: string list;

    mes_adversaires: string array;
    mon_numero: int option;
    nombre_plis_joues: int;
    dp_gagnes: int;
    bouts_remportes: int;
    petit_au_bout: [`Attaque | `Defense] option;
    mon_jeu: Carte.carte list;

    preneur: int option;
    carte_appelee: Carte.carte option;
    joueur_appele: int option;
    chien: Carte.carte list;
    ecart_minimum: Carte.carte list;
    ecart_maximum: Carte.carte list;
    main_maximale: Carte.carte list array;
    encheres: int option array;

    couleur_demandee: Carte.couleur option;
    joueur_ouverture: int;
    devant_jouer: int;
    joueur_maitre: int option;
    plus_gros_atout: Carte.valeur option;
    pli_termine: bool;
    cartes_par_pli: (int * Carte.carte * int) list array;

    appelables: Carte.carte list;
    jouables: Carte.carte list;

    doit_appeler: bool;
    doit_ecarter: bool;
    doit_jouer: bool;
    doit_priser: bool;

    requete_en_cours: bool;
  }

let etat_de client =
  let (dp_gagnes, bouts_gagnes) = points client (joueur_appele client) in
  let mon_jeu = mon_jeu client in
  {
    connecte = est_connecte client;
    mon_nom = mon_nom client;
    en_jeu = en_jeu client;
    joueurs_disponibles = joueurs_disponibles client;
    mes_adversaires = mes_adversaires client;
    mon_numero = mon_numero client;
    nombre_plis_joues = nombre_plis_joues client;
    dp_gagnes = dp_gagnes;
    bouts_remportes = bouts_gagnes;
    petit_au_bout = petit_au_bout client ;
    mon_jeu = mon_jeu;
    preneur = preneur client;
    carte_appelee = carte_appelee client;
    joueur_appele = joueur_appele client;
    chien = chien client;
    ecart_minimum = 
      (match ecart_connu client with 
       | None -> [] 
       | Some x -> x);
    ecart_maximum =
      begin
	match ecart_connu client with
	| Some []
	| None -> List.filter 
		    (fun c -> not (List.exists ((=) c) mon_jeu))
		    client.cartes_non_jouees
	| Some x -> x
      end;
    main_maximale =
      Array.map (fun cartes_interdites ->
		 let pile = ref [] in
		 let () = Array.iteri
			    (fun i_carte interdit ->
			     if not interdit then
			       pile := (Carte.carte_of_int i_carte)
				       :: (!pile)
			     else ())
			    cartes_interdites
		 in
		 List.rev !pile)
		client.interdit;
    encheres = Array.init 5 (enchere_de client);
    couleur_demandee = couleur_demandee client;
    joueur_ouverture = joueur_ouverture client;
    devant_jouer = devant_jouer client;
    joueur_maitre = joueur_maitre client;
    plus_gros_atout = plus_gros_atout client;
    pli_termine = pli_termine client;
    cartes_par_pli = Array.init 15 (repartition_cartes client);
    appelables = cartes_appelables client;
    jouables = cartes_jouables client;
    doit_appeler = doit_appeler client;
    doit_ecarter = doit_ecarter client;
    doit_jouer = doit_jouer client;
    doit_priser = doit_priser client;
    requete_en_cours = requete_en_cours client
  }
